using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using DevExpress.XtraBars;
using System.Linq;
using Pharmacy.Ultilities;
using Pharmacy.Data;
using Pharmacy.Data.Repositories;
using Pharmacy.Data.Entity;



namespace PharmacyPharnet.Core
{
    /// <summary>
    /// Provides helper for IView, used to store shared code of classes that inherite from IView.
    /// This works as an abstract class because the DialogViewBase and ControlViewBase already
    /// inherited from XtraForm, XtraUserControl.    
    public static class ViewHelper
    {
        /// <summary>
        /// Applies current logging in user's permission to all toolbars on this View.
        /// </summary>
        public static void ApplyUserPermissionOnToolBar(IView view)
        {
            // get ALL fields declared in this object
            BarButtonItem[] barItems = LinqToObject
                .From(view.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                // where the item is DevExpress Bar Manager
                .Where( t => t.FieldType == typeof (BarManager))
                // get value of bar manager objects
                .Select<BarManager>(delegate(FieldInfo t) { return (BarManager) t.GetValue(view); })
                // get bar in bar mananger
                .SelectMany<Bar>(delegate(BarManager t) { return t.Bars; })
                // select visible bars only
                .Where(delegate(Bar t) { return t.Visible; })
                // get all their children                
                .SelectMany<LinkPersistInfo>(delegate(Bar t) { return t.LinksPersistInfo; })
                // select .Item property of LinksPersistInfo
                .Select<BarItem>(delegate(LinkPersistInfo t) { return t.Item; })
                // t.Item should not be null
                .DebugAssert(delegate(BarItem t) { return t != null; })
                // only get BarButtonItem
                .OfType<BarButtonItem>()
                .ToArray();

            // check there is no bar item to apply
            if (barItems.Length == 0)
            {
                // do nothing
                return;
            }            

            // prepare function configs
            Dictionary<string, bool> functionConfigs =
                CacheManager.GetValue<Dictionary<string, bool>>(
                    delegate
                        {
                            return LinqToObject.From(GetFunctionNamesOfType(view)).Distinct()
                                                .ToDictionary<string, bool>(t => t.Name, t => t.IsVisible.GetValueOrDefault());
                        }, typeof (ViewHelper), view.GetType(), "functionConfigs");
     
            // prepare function permissions
            Dictionary<string, bool> functionPermissions =
                CacheManager.GetValue<Dictionary<string, bool>>(
                    delegate
                        {
                            return LinqToObject.From(GetAvailableFunctionNamesOfType(view))
                                                .ToDictionary<string, bool>(t => t.Name, t => t.IsVisible.GetValueOrDefault());
                        }, typeof (ViewHelper), view.GetType(), "functionPermissions");

            // process each BarButtonnItem in foreach for better debugging
            foreach (BarButtonItem item in barItems)
            {
                // assume the item is not null
                Debug.Assert(item != null);

                // in default, the button is disabled
                item.Enabled = false;                
                
                if (item.Visibility == BarItemVisibility.Never)
                {
                    //Logger.WriteError(string.Format("{0}[{1}].{2} == {3}", view.GetType().FullName, item.Name, "Visibility", item.Visibility));
                    continue;
                }

                // TODO: workaround for below issue
                if (item.Tag == null)
                {
                    // allow customer to use function while it still has problem
                    item.Enabled = true;                    
                    // continue to next button
                    continue;
                }

                string tagValue = (string) item.Tag;

                // if the right is defined, show or hide based on definition
                if (functionConfigs.ContainsKey(tagValue))
                {
                    item.Visibility = functionConfigs[tagValue] ? BarItemVisibility.Always : BarItemVisibility.Never;
                    item.Enabled = functionPermissions.ContainsKey(tagValue);
                }
                else
                {
                    // if not defined, show and disable
                    // always show toolbars icon, as requested by HungLe
                    item.Visibility = BarItemVisibility.Always;
                    // ThangTran - changed to true, because there are too many missing function definition in the database
                    item.Enabled = false;
                }

                // special case, current logging user is in admin group                
                if (Global.CurrentUserIsAdmin)
                {
                    // allow full access
                    item.Enabled = true;
                }
            }
        }

        /// <summary>
        /// Gets list of function names of specified View Type.
        /// </summary>
        /// <returns></returns>
        public static Function[] GetFunctionNamesOfType(IView view)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(view != null);
            // --- End parameters checking code -------------------------------

            // get type of the view
            string viewTypeInfo = view.GetType().FullName;
            using (IObjectContext pharmacyContext = ObjectContextFactory.CreatePharmacyContext(Global.UserName, Global.CurrentConnectionString))
            {
                TypeInfoRepository rTypeInfo = new TypeInfoRepository(pharmacyContext);
                return rTypeInfo.AsQueryable()
                    // get all DIS_TypeInfo where TypeInfo is FullName of view type
                    .Where(t => t.Name == viewTypeInfo)
                    // get all FunctionTypeInfos
                    .SelectMany<TypeInfo, FunctionTypeInfo>(delegate(TypeInfo t) { return t.FunctionTypeInfoes; })
                    // get all Functions
                    .Select<FunctionTypeInfo, Function>(delegate(FunctionTypeInfo t) { return t.Function; })
                    .ToArray();
            }
        }

        /// <summary>
        /// Gets list of function names of specified View Type that can be used by current logging user.
        /// </summary>C
        /// <returns></returns>
        public static Function[] GetAvailableFunctionNamesOfType(IView view)
        {
            // --- Begin parameters checking code -----------------------------
            Debug.Assert(view != null);
            // --- End parameters checking code -------------------------------

            // get functions IDs related to this view to optimize performance
            int[] functionIDs =
                LinqToObject.From(GetFunctionNamesOfType(view)).Select<int>(t => t.ID)
                    .ToArray();
            using (IObjectContext pharmacyContext = ObjectContextFactory.CreatePharmacyContext(Global.UserName, Global.CurrentConnectionString))
            {
                UserRepository rUser = new UserRepository(pharmacyContext);
                PermissionAssignmentRepository rPermission = new PermissionAssignmentRepository(pharmacyContext);
                int[] roleIDs = rUser.AsQueryable()
                    // select top 1 * from DIO_User where ID = Global.UserID
                    .Where(u => u.ID == Global.UserID)
                    // inner join DIO_UserRole
                    .SelectMany<User, UserRole>(delegate(User u) { return u.UserRoles; })
                    // get all RoleID, prepare to get related Functions
                    .Select(u => u.Role.ID)
                    .ToArray();
                // Get list PermisstionAssigment by roleIDs
                IEnumerable<PermissionAssignment> lst1 = from p in rPermission.AsQueryable().AsEnumerable()
                                                         where roleIDs.Contains(p.Role.ID)
                                                         select p;
                // Get List PermissionAssigment by Function
                IEnumerable<PermissionAssignment> lst2 = from p in lst1
                                                         where (functionIDs.Contains(p.Function.ID))
                                                         select p;
                // Get functions
                Function[] lstFunction = LinqToObject.From<PermissionAssignment>(lst2)
                                                    .Select<PermissionAssignment, Function>(p => p.Function)
                                                    .Distinct()
                                                    .ToArray();
                return lstFunction;
            }
        }        
    }
}
